﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using PluginInterface;

/* Плагин для подсветки кода на языке C++ 
 * Основной метод SourcetoHtml(string input) принимает код программы
 * и анализирует исходный код, обрамляя значимые лексемы тэгами
 * 
 * По умолчанию используется внутренная таблица стилей, но есть возможность 
 * использовать собственную таблицу.
 * Для этого необходимо передать полное имя css таблицы через свойство CSSPath 
 * 
 * [Весь код формируется в плагине и предается как строка] 
 */

/* 1 : predcompil comands    
 * 2 : key words
 * 3 : string    
 * 4 : comments  
 */

namespace CppPlugin
{
    /// <summary>
    ///Класс - плагин для языка С++ анализирует исходный код и обраляет значимые лексемы тегами 
    /// </summary>
    public class CppPlugin : IPlugin
    {

        #region internal structure

        private string cssPath = "";            // путь к пользовательской таблице css 
        private string outString = "";          // трансформированный код

        List<TextType> textBlockList = new List<TextType>();  //временное хранилище "типизированных" блоков 

        /// <summary>
        /// Экранирует теги в исходных кодах
        /// </summary>
        /// <param name="inputStr">исходный код программы</param>
        /// <returns>код с экранированными тегами</returns>
        public string TagBlock(string inputStr)
        {
            string result = "";
            for (int i = 0; i < inputStr.Length; i++)
            {
                if (inputStr[i] == '<')
                {
                    result += "&lt";
                    continue;
                }
                if (inputStr[i] == '>')
                {
                    result += "&gt";
                    continue;
                }
                result += inputStr[i];
            }
            return result;
        }

        /// <summary>
        /// Анализирует исходный код и формирует набор распознанных лексем
        /// </summary>
        /// <param name="inputStr"> исходный код одной строкой</param>
        public void Analizing(string inputStr)
        {
            Regex word = new Regex(@"\b[a-z]([a-z]){1,}\b");
            //    "(.\".*\")|(@\"[.\n\r\t]*?\")|(\"\")|(\".*?[^\\\\]\")"
            Regex str = new Regex("(\".*?\")|(@\".*?\")|(@\"(.|\r|\n)*?\")|(\"\")|(\".*?[^\\\\]\")");
            Match fr;

            int size = inputStr.Length;
            int start;
            int end;

            for (int index = 0; index < size; index++)
            {
                if (inputStr[index] == ' ')
                    continue;

                #region Поиск коментариев
                if (inputStr[index] == '/')
                {
                    if ((index + 1) < size)
                    {
                        if (inputStr[index + 1] == '/')
                        {
                            start = index;
                            while (index < size)
                            {
                                if ((inputStr[index] == '\n'))
                                    break;
                                index++;
                            }
                            end = index;
                            textBlockList.Add(new TextType(start, end, 4));
                            start = -1;
                            end = -1;
                            continue;
                        }

                        if (inputStr[index + 1] == '*')
                        {
                            start = index;
                            index += 2;
                            while (index + 1 < size)
                            {
                                if ((inputStr[index] == '*') && (inputStr[index + 1] == '/'))
                                {
                                    index++;
                                    break;
                                }
                                else
                                    index++;
                            }
                            ++index;
                            end = index;
                            textBlockList.Add(new TextType(start, end, 4));
                            start = -1;
                            start = -1;
                            continue;
                        }
                    }
                }
                #endregion

                #region Поиск строк
                if (inputStr[index] == '"')
                {
                    fr = str.Match(inputStr, index);
                    if (fr.Success)
                    {
                        if (fr.Index == index)
                        {
                            index = fr.Index;
                            textBlockList.Add(new TextType(index, (index + fr.Value.Length), 3));
                            index += fr.Value.Length;
                            continue;
                        }
                    }
                }
                #endregion

                #region Поиск ключевых слов и директив предкомпилятора
                if ((inputStr[index] != ' ') && (inputStr[index] != '\n') && (inputStr[index] != ':') &&
                    (inputStr[index] != ';') && (inputStr[index] != '.') && (inputStr[index] != ',') &&
                    (inputStr[index] != '\r'))
                {
                    fr = word.Match(inputStr, index);
                    if (fr.Success)
                    {
                        if (fr.Index == index)
                        {
                            if (PreprocessorWord.isPreprocWord(fr.Value) && (inputStr[fr.Index - 1] == '#'))
                            {
                                index = fr.Index;
                                textBlockList.Add(new TextType(index - 1, (index + fr.Value.Length), 1));
                                index += fr.Value.Length;
                                continue;
                            }
                            else if (KeyWord.isKeyWord(fr.Value))
                            {
                                index = fr.Index;
                                textBlockList.Add(new TextType(index, (index + fr.Value.Length), 2));
                                index += fr.Value.Length;
                                continue;
                            }
                        }
                    }
                }
                #endregion
            }

        }


        /// <summary>
        /// Обрамляет лексему тегами соответствующими типу данной лексемы  
        /// </summary>
        /// <param name="block">лексема</param>
        /// <param name="inputStr">исходный код</param>
        /// <returns></returns>
        public string TypeToTag(TextType block, string inputStr)
        { //применяет css тег к "типам"
            string st = "";
            string end = "</span>";

            switch (block.Type)
            {
                case 1: st = "<span class=\"dirc\">";
                    break;
                case 2: st = "<span class=\"keyw\">";
                    break;
                case 3: st = "<span class=\"str\">";
                    break;
                case 4: st = "<span class=\"comt\">";
                    break;
            }
            return string.Concat(st, inputStr.Substring(block.StartPosition, block.EndPosition - block.StartPosition), end);
        }


        /// <summary>
        /// Анализирует исходный код переданный в качестве параметра и обрамляет тегами найденные лексемы 
        /// </summary>
        /// <param name="inputStr">исходный код</param>
        public void CreateHtmlString(string inputStr)
        {
            int size = textBlockList.Count - 1;

            if (textBlockList.Count == 0)
            {
                this.outString += inputStr;
                return;
            }

            if (textBlockList[0].StartPosition == 0)
            {
                this.outString += TypeToTag(textBlockList[0], inputStr);
            }
            else
            {
                this.outString += string.Concat(inputStr.Substring(0, textBlockList[0].StartPosition),
                    TypeToTag(textBlockList[0], inputStr));
            }
            int b1, b2;
            for (int i = 1; i <= size; i++)
            {
                b1 = textBlockList[i - 1].EndPosition;
                b2 = textBlockList[i].StartPosition;
                if ((b2 - b1) > 0)
                {
                    this.outString += inputStr.Substring(b1, b2 - b1);
                }
                this.outString += TypeToTag(textBlockList[i], inputStr);
            }
            if (textBlockList[size].EndPosition < (inputStr.Length - 1))
            {
                this.outString += inputStr.Substring(textBlockList[size].EndPosition);
            }

        }

        #endregion

        public string _String
        {
            get
            {
                return this.outString;
            }
        }

        //Реализация интерфейса IPlugin
        #region external structure

        /// <summary>
        /// Формирует тело html-документа на основе CSS и обработанного исходного кода 
        /// </summary>
        /// <param name="input">исходный код обрамленный тегами</param>
        /// <returns>код готового html-документа</returns>
        public string SourcetoHtml(string input)
        { // анализ и трансформирование

            this.outString = "";
            this.textBlockList.Clear();
            string inputStr = TagBlock(input);

            outString += "<HTML>  ";
            outString += "<HEAD>  ";

            if (this.CSSPath != "")
            {
                outString = outString + "link rel=\"stylesheet\" type=\"text/css\" href=\"" + this.cssPath + ".css\"> \n";
            }
            else
            {
                outString += DefaultCSS.sheet;
            }
            outString += "<pre>";
            Analizing(inputStr);
            CreateHtmlString(inputStr);

            outString += "</pre>";
            outString += "</HEAD>  ";
            outString += "</HTML>  ";

            return outString;
        }

        /// <summary>
        /// Свойство возвращает название плагина
        /// </summary>
        public string Name { get { return "C++ plugin"; } }

        /// <summary>
        /// Свойство возвращает название языка программирования для которого предназначен данный плагин
        /// </summary>
        public string languageName { get { return "C++"; } }

        /// <summary>
        /// Свойство возвращает разширение для исходных кодов на C++ (.cpp)
        /// </summary>
        public string Extension { get { return "cpp"; } }

        /// <summary>
        /// Свойство предназначенное для подключения собственных CSS
        /// </summary>
        public string CSSPath
        {
            get { return this.cssPath; }
            set { this.cssPath = value; }
        }

        #endregion


        public bool Injection
        {
            get
            {
                return false;
            }
            set
            {
                throw new NotImplementedException();
            }
        }
    }
}
